package com.cc.zk.distribute.idgen;

import com.cc.zk.distribute.zookeeper.ZKClient;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.curator.framework.CuratorFramework;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * ID 生成器
 *
 * @author cc
 * @date 2022/08/24
 */
@Data
@Slf4j
public class IdWorker {


    /**
     * Zk客户端
     */
    private CuratorFramework client = null;

    /**
     * 工作节点的路径
     */
    private String pathPrefix = "/push-center/IDMaker/worker-";
    public static final String DEFAULT_PATH_PREFIX = "/push-center/name-service/worker-";
    private String pathRegistered = null;
     /**
     * 是否初始化了
     */
    private volatile boolean inited = false;
     /**
     * 吧每个服务的 工作id 缓存起来
     */
    public static final Map<String, IdWorker> CACHE = new ConcurrentHashMap<>();
     /**
     * 每个服务启动都会生成一个工作节点id
     */
    private long workId;


    private IdWorker(String pathPrefix) {
        this.pathPrefix = pathPrefix;

    }

    public static synchronized IdWorker getInstance() {
        return getInstance(DEFAULT_PATH_PREFIX);
    }

    public static synchronized IdWorker getInstance(String workerPath) {

        if (CACHE.containsKey(workerPath)) {
            return CACHE.get(workerPath);
        }

        IdWorker worker = new IdWorker(workerPath);
        CACHE.put(workerPath, worker);
        return worker;
    }


    /**
     * 在zookeeper中创建临时节点并写入信息
     */
    public synchronized void init() {
        client = ZKClient.getSingleton().getClient();

        //处理 节点id不回收。如果当前节点下面没有子节点就把删除了重新创建
        //滚动升级 无法使用此方案，因为 主节点下面始终有子节点不能删除。
        ZKClient.getSingleton().deleteWhenHasNoChildren(pathPrefix);

        createZNode();

        //获取到节点的id
        String sid = null;
        if (null == pathRegistered) {
            throw new RuntimeException("节点注册失败:" + pathPrefix);
        }
        int index = pathRegistered.lastIndexOf(pathPrefix);
        if (index >= 0) {
            index += pathPrefix.length();
            //从在zk创建的path上 截取出 zk自动生成的有序id，设置为节点id
            sid = index <= pathRegistered.length() ? pathRegistered.substring(index) : null;
        }

        if (StringUtils.isBlank(sid)) {
            throw new RuntimeException("节点ID生成失败");
        }

        //设置为节点id
        workId = Long.parseLong(sid);
        log.info("the worker of type{} id  is {}", pathPrefix, workId);
        inited = true;
    }


    private void createZNode() {
        // 创建一个 ZNode 节点
        // 节点的 payload 为当前worker 实例
        pathRegistered = ZKClient.getSingleton().createEphemeralSeqNode(pathPrefix);

    }

    /**
     * 获取 当前服务实例的workId
     */
    public long getId() {
        if (!inited) {
            init();
        }
        return workId;
    }
}